﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Web;

namespace Sharp
{
    public interface IUser<U>
    {
        String email { get; set; }
        String password { get; set; }
        long id { get; set; }
        DateTime? last_authentication { get; set; }
        U UpSert();
        /// <summary>
        /// Get user by ID
        /// </summary>
        /// <param name="id"></param>
        U GetUser(long id, StateBag val);
        /// <summary>
        /// Get User by email
        /// </summary>
        /// <param name="email"></param>
        U GetUser(string email, StateBag val);
    }


    public enum BrowserTypes
    {
        Desktop = 1, Touch, Mobile
    }


    public class Session<U> where U : IUser<U>, new()
    {
        public Session(HttpContext context, StateBag sb)
        {
            GetUser(context, sb);
        }

        public Session(U user)
        {
            _CurrentUser = user;
        }

        public string Cookie { get; set; }

        private U _CurrentUser { get; set; }
        public U CurrentUser
        {
            get
            {
                return _CurrentUser;
            }
            set
            {
                _CurrentUser = value;
            }

        }

        //[DataMember]
        public Boolean IsAuthed { get { return (this.TokenTimeStamp ?? DateTime.Now.AddYears(-1)).AddHours(23) > DateTime.Now; } }
        //[DataMember]
        public String EncryptedID { get { return EncryptUserID(CurrentUser.id); } }

        public DateTime? TokenTimeStamp { get; set; }

        public Comet.AsyncRequestState CometState { get; set; }

        public String Password { get { return new SimpleAES().DecryptString(CurrentUser.password); } set { CurrentUser.password = new SimpleAES().EncryptToString(value); } }

        public string EncryptUserID(long id)
        {
            return new SimpleAES().EncryptToString(DateTime.Now + "-" + id);
        }
        private long DecryptUserID(string id)
        {
            string token = new SimpleAES().DecryptString(id); 
            string[] tokens = token.Split("-");
            TokenTimeStamp = DateTime.Parse(tokens[0].Trim());
            return (tokens.Length > 1 ? tokens[1] : "").ToLong(0);
        }

        private string CreateConfirmationToken()
        {
            return EncryptedID;
        }
        public long DecryptConfirmationToken(string id)
        {
            return DecryptUserID(id);
        }

        //        public void SendConfirmationEmail()
        //        {
        //            Email msg = new Email();
        //            msg.To.Add(new MailAddress(this.email, this.Name));
        //            msg.From = new MailAddress("support@servercyde.com", "ServerCyde.com Team");
        //            msg.Body = string.Format(@"Hello {0},
        //
        //In order to use your account you must confirm the email address.
        //
        //Activate your account by clicking this link:
        //http://servercyde.com/confirm/?token={1}
        //
        //If you have any questions please reply to this email.
        //
        //Thanks,
        //The ServerCyde Minions: Riaz and Amir
        //support@servercyde.com", this.Name, this.CreateConfirmationToken());
        //            msg.Subject = "Please confirm your account";
        //            msg.IsBodyHtml = false;
        //            msg.Send(val);
        //        }

        /// <summary>
        /// Assigns a user to CurrentUser
        /// </summary>
        /// <param name="context"></param>
        /// <param name="val"></param>
        private void GetUser(HttpContext context, StateBag val)
        {
            bool has_session = context.Request.Cookies["Identity"] != null;

            string cookie = has_session ? context.Request.Cookies["Identity"].Value : null;
            
            try
            {
                CurrentUser = (U)(cookie == null ? new U() : (HttpRuntime.Cache[cookie] ?? new U().GetUser(DecryptUserID(cookie), val)));
            }
            catch (Exception e)
            {
                context.Response.Cookies["Identity"].Expires = DateTime.MinValue;
                CurrentUser = new U();
            }

            if (CurrentUser.id != 0)
            {
                if (HttpRuntime.Cache[EncryptedID] == null)
                    HttpRuntime.Cache.Add(EncryptedID, CurrentUser, null, System.Web.Caching.Cache.NoAbsoluteExpiration, new TimeSpan(1, 0, 0, 0), System.Web.Caching.CacheItemPriority.BelowNormal, null);

                //update DB if session is getting old but still valid
                if (((TokenTimeStamp ?? DateTime.MinValue).AddHours(12) < DateTime.Now || cookie == null) && (TokenTimeStamp ?? DateTime.MinValue).AddHours(24) > DateTime.Now)
                {
                    NewSession(context);
                }
            } 
        }

        public void NewSession(HttpContext context, bool rememberme)
        {
            HttpCookie cookie = new HttpCookie("Identity", this.EncryptedID);
            cookie.Expires = DateTime.Now.AddDays(rememberme ? 365 : 1);
            context.Response.Cookies.Add(cookie);
            cookie = new HttpCookie("RememberMe", "true");
            context.Response.Cookies.Add(cookie);
            CurrentUser.last_authentication = DateTime.Now.AddDays(rememberme ? 365 : 0);
            CurrentUser.UpSert();
        }

        public void NewSession(HttpContext context)
        {
            bool rememberme = (context.Request.Cookies["RememberMe"] ?? new HttpCookie("null", "false")).Value.ToBool(); //get stored setting or false
            NewSession(context, rememberme);
        }

        public void SignOut(HttpContext context)
        {
            CurrentUser.last_authentication = DateTime.Now.AddDays(-366);
            CurrentUser.UpSert();

            HttpCookie cookie = new HttpCookie("Identity", this.EncryptedID);
            cookie.Expires = DateTime.Now.AddDays(-1D);
            context.Response.Cookies.Add(cookie);
        }


    }
}
